package object

import (
	"fmt"
	"strings"
)

// Builtings 内置函数匿名结构体切片
var Builtings = []struct {
	Name    string
	Builtin *Builtin
}{
	{
		Name:    "len",
		Builtin: &Builtin{Fn: builtinLen},
	},
	{
		Name:    "print",
		Builtin: &Builtin{Fn: builtinPrint},
	},
	{
		Name:    "first",
		Builtin: &Builtin{Fn: builtinFirst},
	},
	{
		Name:    "last",
		Builtin: &Builtin{Fn: builtinLast},
	},
	{
		Name:    "rest",
		Builtin: &Builtin{Fn: builtinRest},
	},
	{
		Name:    "append",
		Builtin: &Builtin{Fn: builtinAppend},
	},
}

// GetBuiltinByName 根据名称获取内置函数
func GetBuiltinByName(name string) *Builtin {
	for _, def := range Builtings {
		if def.Name == name {
			return def.Builtin
		}
	}
	return nil
}

// newError 构建一个object.Error指针
func newError(format string, a ...interface{}) *Error {
	return &Error{Message: fmt.Sprintf(format, a...)}
}

// builtinAppend 内置函数 - 向数组或者hash的末尾追加一个元素
func builtinAppend(args ...Object) Object {
	if len(args) < 2 {
		return newError("wrong number of arguments. got=%d", len(args))
	}
	switch args[0].Type() {
	case StringObj:
		return appendString(args...)
	case ArrayObj:
		return appendArray(args...)
	case HashObj:
		return appendHash(args...)
	default:
		return newError("argument to `append` not supported, got %s", args[0].Type())
	}
}

// appendString 向字符串末尾追加一个元素
func appendString(args ...Object) Object {
	if args[0].Type() != StringObj {
		return newError("argument to `append` not supported, got %s", args[0].Type())
	}
	str := args[0].(*String)
	appendLen := len(args)
	value := str.Value

	var builder strings.Builder
	builder.WriteString(value)

	for i := 1; i < appendLen; i++ {
		builder.WriteString(args[i].Inspect())
	}

	return &String{Value: builder.String()}
}

// appendHash 向hash的末尾追加一个元素
func appendHash(args ...Object) Object {
	if (len(args) & 1) != 1 {
		return newError("wrong number of arguments. got=%d, want odd number", len(args))
	}
	if args[0].Type() != HashObj {
		return newError("argument to `append` not supported, got %s", args[0].Type())
	}

	hash := args[0].(*Hash)
	appendLen := len(args)
	for i := 1; i < appendLen; i += 2 {
		// 1. key 是否能够作为 key
		key, ok := args[i].(Hashable)
		if !ok {
			return newError("unusable as hash key: %s", args[1].Type())
		}
		hk := key.HashKey()
		// 2. key 是否已经存在
		pair, ok := hash.Pairs[hk]
		if !ok {
			pair = HashPair{Key: args[i]}
		}
		pair.Value = args[i+1]
		hash.Pairs[hk] = pair
	}

	return hash
}

// appendArray 向数组的末尾追加一个元素
func appendArray(args ...Object) Object {
	if args[0].Type() != ArrayObj {
		return newError("argument to `append` not supported, got %s", args[0].Type())
	}

	arr := args[0].(*Array)
	length := len(arr.Elements)
	appendLen := len(args) - 1

	newEle := make([]Object, length+appendLen, length+appendLen)
	copy(newEle, arr.Elements)

	j := 1
	for i := length; i < length+appendLen; i++ {
		newEle[i] = args[j]
		j++
	}

	return &Array{Elements: newEle}
}

// builtinRest 内置函数 - 传入一个数组做为参数, rest 会返回一个新数组，其中包含除了原数组第一个元素之外的其他所有元素，即干掉原数组中的第一个元素
func builtinRest(args ...Object) Object {
	if len(args) != 1 {
		return newError("wrong number of arguments. got=%d, want=1",
			len(args))
	}

	if args[0].Type() != ArrayObj {
		return newError("argument to `rest` must be ARRAY, got %s",
			args[0].Type())
	}

	arr := args[0].(*Array)
	length := len(arr.Elements)
	if length > 0 {
		newEle := make([]Object, length-1, length-1)
		copy(newEle, arr.Elements[1:length])
		return &Array{Elements: newEle}
	}

	return nil
}

// builtinLast 内置函数 - 返回最后一个元素
func builtinLast(args ...Object) Object {
	if len(args) != 1 {
		return newError("wrong number of arguments. got=%d, want=1", len(args))
	}
	switch arg := args[0].(type) {
	case *String:
		if len(arg.Value) > 0 {
			return &String{Value: arg.Value[len(arg.Value)-1:]}
		}
		return nil
	case *Array:
		if len(arg.Elements) > 0 {
			return arg.Elements[len(arg.Elements)-1]
		}
		return nil
	case *Tuple:
		if len(arg.Elements) > 0 {
			return arg.Elements[len(arg.Elements)-1]
		}
		return nil
	// TODO 其他类型
	default:
		return newError("argument to `last` not supported, got %s", args[0].Type())
	}
}

// builtinFirst 内置函数 - 返回第一个元素
func builtinFirst(args ...Object) Object {
	if len(args) != 1 {
		return newError("wrong number of arguments. got=%d, want=1", len(args))
	}
	switch arg := args[0].(type) {
	case *String:
		if len(arg.Value) > 0 {
			return &String{Value: arg.Value[0:1]}
		}
		return nil
	case *Array:
		if len(arg.Elements) > 0 {
			return arg.Elements[0]
		}
		return nil
	case *Tuple:
		if len(arg.Elements) > 0 {
			return arg.Elements[0]
		}
		return nil
	// TODO 其他类型
	default:
		return newError("argument to `first` not supported, got %s", args[0].Type())
	}
}

// builtinPrint 内置函数 - 对外输出
func builtinPrint(args ...Object) Object {
	for _, arg := range args {
		fmt.Println(arg.Inspect())
	}
	return nil
}

// builtinLen 内置函数 len
func builtinLen(args ...Object) Object {
	if len(args) != 1 {
		return newError("wrong number of arguments. got=%d, want=1", len(args))
	}
	switch arg := args[0].(type) {
	case *String:
		return &Integer{Value: int64(len(arg.Value))}
	case *Array:
		return &Integer{Value: int64(len(arg.Elements))}
	case *Hash:
		return &Integer{Value: int64(len(arg.Pairs))}
	case *Tuple:
		return &Integer{Value: int64(len(arg.Elements))}
	// TODO 其他类型
	default:
		return newError("argument to `len` not supported, got %s", args[0].Type())
	}
}
